﻿using System;
using System.IO.Ports;
using MyClass;

namespace SerialCommunication
{
    public partial class SerialFrame
    {
        public struct RecDataFrame
        {
            public int reclength;
            public byte[] rectext;
        }

        public SerialPort serialPort1 = new SerialPort();
        
        public byte[] ParseBuffer = new byte[1024];

        protected int TextHead = 0;
        protected bool NotShift, HaveHead;
        protected const byte DLE = 0x10;
        protected const byte STX = 0x02;
        protected const byte ETX = 0x03;

        protected const int FRAME_NUMBER_MAX = 300;
        protected const int FRAME_LENGTH_MAX = 1024;

        public int RecHead, RecTail;
        public RecDataFrame[] RecFrame = new RecDataFrame[FRAME_NUMBER_MAX];

        public SerialFrame()
        {
            for (int i = 0; i < FRAME_NUMBER_MAX; i++)
            {
                RecFrame[i].reclength = 0;
                RecFrame[i].rectext = new byte[1024];
            }
            RecHead = 0;
            RecTail = 0;
            serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        }

        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] tmparray = new byte[1024];
            SerialPort sp = (SerialPort)sender;
            int Length = sp.Read(tmparray, 0, sp.BytesToRead);
            Parse_Serial_Data1(tmparray, Length);
        }

        private void Parse_Serial_Data1(byte[] SBuf, int length)
        {
            int j;
            int i = length;

            for (j = 0; j < i; j++)
            {
                switch (SBuf[j])
                {
                    case STX:
                        if ((TextHead > 0) && (ParseBuffer[TextHead - 1] == DLE) && NotShift)
                        {
                            HaveHead = true;
                            ParseBuffer[0] = DLE;
                            ParseBuffer[1] = STX;
                            TextHead = 2;
                        }
                        else
                        {
                            if (TextHead < FRAME_LENGTH_MAX)
                            {
                                if (TextHead < 0)
                                    TextHead = 0;
                                ParseBuffer[TextHead] = SBuf[j];
                                TextHead++;
                            }
                        }
                        break;
                    case ETX:
                        if ((TextHead > 0) && (ParseBuffer[TextHead - 1] == DLE) && NotShift)
                        {
                            if (TextHead < FRAME_LENGTH_MAX)
                            {
                                if (TextHead < 0)
                                    TextHead = 0;
                                ParseBuffer[TextHead] = SBuf[j];
                                TextHead++;
                            }
                            if (HaveHead)
                            {
                                Parse_Data((ushort)(TextHead));
                            }
                            HaveHead = false;
                            TextHead = 0;
                        }
                        else
                        {
                            if (TextHead < FRAME_LENGTH_MAX)
                            {
                                if (TextHead < 0)
                                    TextHead = 0;
                                ParseBuffer[TextHead] = SBuf[j];
                                TextHead++;
                            }
                        }
                        break;
                    case DLE:
                        if ((TextHead < 1) || ((TextHead >= 0) && (ParseBuffer[TextHead - 1] != DLE)) || (!NotShift))
                        {
                            if (TextHead < FRAME_LENGTH_MAX)
                            {
                                if (TextHead < 0) TextHead = 0;
                                ParseBuffer[TextHead] = SBuf[j];
                                NotShift = true;
                                TextHead++;
                            }
                        }
                        else
                        {
                            NotShift = false;
                        }
                        break;
                    default:
                        if (TextHead < FRAME_LENGTH_MAX)
                        {
                            if (TextHead < 0)
                                TextHead = 0;
                            ParseBuffer[TextHead] = SBuf[j];
                            NotShift = true;
                            TextHead++;
                        }
                        else
                        {
                            TextHead = 0;
                        }
                        break;
                }
            }
        }

        private void Parse_Data(ushort datalength)
        {
            int i;
            byte[] databuffer = new byte[1024];

            ParseBuffer.CopyTo(databuffer, 0);
            if (datalength > 8)
            {
                RecFrame[RecHead].reclength = datalength;
                for (i = 0; i < datalength; i++)
                {
                    RecFrame[RecHead].rectext[i] = databuffer[i];
                }
                RecHead++;
                if (RecHead >= FRAME_NUMBER_MAX)
                {
                    RecHead = 0;
                }
            }
        }

        public void SendCmd(byte cmdType, byte cmd, byte[] cmdData, uint dataIndexOffset, uint cmdDataLen)
        {
            uint i;
            ushort crc;

            byte[] cmdBuf = new byte[cmdDataLen + 14];

            cmdBuf[0] = DLE;
            cmdBuf[1] = STX;
            cmdBuf[2] = (byte)((8 + cmdDataLen) / 256);
            cmdBuf[3] = (byte)((8 + cmdDataLen) % 256);
            cmdBuf[4] = 0x02;
            cmdBuf[5] = 0x00;
            cmdBuf[6] = 0x06;
            cmdBuf[7] = 0x00;
            cmdBuf[8] = cmdType;
            cmdBuf[9] = cmd;

            if (cmdDataLen > 0 && cmdData != null)
            {
                for (i = 0; i < cmdDataLen; i++)
                    cmdBuf[10 + i] = cmdData[i + dataIndexOffset];
            }

            crc = GF.CRC(cmdBuf, 2, (ushort)(8 + cmdDataLen));
            cmdBuf[cmdDataLen + 10] = (byte)(crc / 256);
            cmdBuf[cmdDataLen + 11] = (byte)(crc % 256);
            cmdBuf[cmdDataLen + 12] = DLE;
            cmdBuf[cmdDataLen + 13] = ETX;

            byte[] cmdBufAdded10 = new byte[1024];
            int cmdLen = Add0x10toCmdBuf(cmdBuf, (int)(cmdDataLen + 14), cmdBufAdded10);
            if (serialPort1.IsOpen)
            {
                this.serialPort1.Write(cmdBufAdded10, 0, cmdLen);
            }
        }

        private int Add0x10toCmdBuf(byte[] cmdBuf, int cmdLen, byte[] newCmdBuf)
        {
            int i = 0, j = 2;

            newCmdBuf[0] = 0x10;
            newCmdBuf[1] = 0x02;

            for (i = 0; i < cmdLen - 4; i++)
            {
                newCmdBuf[j++] = cmdBuf[i + 2];
                if (cmdBuf[i + 2] == 0x10)
                {
                    newCmdBuf[j++] = 0x10;
                }
            }
            newCmdBuf[j++] = 0x10;
            newCmdBuf[j++] = 0x03;
            return j;
        }

        public void ClosePort()
        {
            serialPort1.Close();
        }

        public void OpenPort(string portname)
        {
            serialPort1.BaudRate = 9600;
            serialPort1.PortName = portname;
            serialPort1.Open();
            
        }

        public void RecTailIncrese()
        {
            RecTail++;
            if (RecTail >= FRAME_NUMBER_MAX) RecTail = 0;
        }

        public bool IsEmpty()
        {
            if (RecTail == RecHead)
                return true;
            else
                return false;
        }

        public bool IsOpen()
        {
            if (serialPort1.IsOpen)
                return true;
            else
                return false;
        }







    ////////////////////////////////////////////////////////////
    }

}